package spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

public class LearnSpringAOP {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        HelloService helloService = context.getBean(HelloService.class);

        System.out.println("=== 调用 sayHello ===");
        helloService.sayHello();

        System.out.println("\n=== 调用 sayHi ===");
        String result = helloService.sayHi("张三");
        System.out.println("返回值: " + result);

        System.out.println("\n=== 调用 throwException ===");
        try {
            helloService.throwException();
        } catch (Exception e) {
            System.out.println("主程序捕获异常：" + e.getMessage());
        }

        System.out.println("\n=== 实际类名 ===");
        System.out.println(helloService.getClass());
    }
}

@ComponentScan
@Configuration
@EnableAspectJAutoProxy
class Config {}

@Service
class HelloService {
    public void sayHello() {
        System.out.println("Hello, 同学！");
    }

    public String sayHi(String name) {
        System.out.println("Hi, " + name + "！");
        return "你好, " + name;
    }

    public void throwException() {
        System.out.println("准备抛出异常...");
        throw new RuntimeException("出错啦！");
    }
}

@Aspect
@Component
class LoggingAspect {

    // ✅ 抽取通用切点表达式（可复用）
    @Pointcut("execution(* spring.aop.HelloService.*(..))")
    public void allMethods() {}

    // ✅ 前置通知：执行前打印方法信息
    @Before("allMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("[Before] 方法：" + joinPoint.getSignature().getName());
    }

    // ✅ 后置通知：无论是否异常都会执行（类似 finally）
    @After("allMethods()")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("[After] 方法：" + joinPoint.getSignature().getName());
    }

    // ✅ 返回后通知：方法正常返回才执行
    @AfterReturning(pointcut = "allMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("[AfterReturning] 方法：" + joinPoint.getSignature().getName() + "，返回值：" + result);
    }

    // ✅ 异常通知：方法抛异常才执行
    @AfterThrowing(pointcut = "allMethods()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Throwable ex) {
        System.out.println("[AfterThrowing] 方法：" + joinPoint.getSignature().getName() + "，抛出异常：" + ex.getMessage());
    }

    // ✅ 环绕通知：能完全控制目标方法的执行
    @Around("allMethods()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[Around] 方法开始：" + pjp.getSignature().getName());
        Object result = null;
        try {
            result = pjp.proceed(); // 执行目标方法
            System.out.println("[Around] 方法成功结束：" + pjp.getSignature().getName());
        } catch (Throwable t) {
            System.out.println("[Around] 方法异常：" + t.getMessage());
            throw t; // 继续抛出
        }
        return result;
    }
}
